package com.cloudcentral.util;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * Create by WEI
 * 2023/7/10 18:12
 */
public class RSAUtil {
  /*
   * RSA最大加密明文大小
   * RSA：117
   * RSA2：245
   */
  private static final int MAX_ENCRYPT_BLOCK = 245;
  /*
   * RSA最大解密密文大小
   * RSA：128
   * RSA2：256
   */
  private static final int MAX_DECRYPT_BLOCK = 256;
  private static final int KEY_SIZE = 2048;
  private static final String ALGORITHM = "RSA";
  private static final String PUBLIC_KEY = """
      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiqeRUEnlO8YSL3e5XxqdlGbtDmmAmuIzbMSgQ3iuQ+s2Rt/alAw4DutwZoUZW1ADi6ReHVrCRp23mTvJQik4NIzbkJZo+24U0vgq8u1+YsjvfEoRGnSHpQfckSWR9wwQptsmqaOZKA7cODQ7nn98MHOpXtTuFLM2Za3qeH/uEo4DnvTZ7S/cBLdWRmpTYYAoeR/wi8i4p2+FAaxpiWic/TFWTpoTxf0WjVeIHHQmyOUGd01adFoUAYDmBDz//CXQb6/3gtD64naO4x2rbeQpfyJ1jq1gZvGl3PVsc3pbDwnpqB/tPIrmV0MHWQHvQ8bUThkv5c3EweDYcwBJcjAywIDAQAB
      """;
  private static final String PRIVATE_KEY = """
      MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCyKp5FQSeU7xhIvd7lfGp2UZu0OaYCa4jNsxKBDeK5D6zZG39qUDDgO63BmhRlbUAOLpF4dWsJGnbeZO8lCKTg0jNuQlmj7bhTS+Cry7X5iyO98ShEadIelB9yRJZH3DBCm2yapo5koDtw4NDuef3wwc6le1O4UszZlrep4f+4SjgOe9NntL9wEt1ZGalNhgCh5H/CLyLinb4UBrGmJaJz9MVZOmhPF/RaNV4gcdCbI5QZ3TVp0WhQBgOYEPP/8JdBvr/eC0Prido7jHatt5Cl/InWOrWBm8aXc9WxzelsPCemoH+08iuZXQwdZAe9DxtROGS/lzcTB4NhzAElyMDLAgMBAAECggEANLaDST4ztc7fwnXaws8CuKsAhFwiD8k9Go8GxIQVNme63ilSk2PSciV2DTfHHRMrxoZkp90lo/XegYIfdzeNgK7wZIjg+K1Wwd5LRMjquEBAfOwi28rrTfzJKQe95tjwtmRBx6fLrV3y+Dyr1ELnrRnKSA+q/3gP/7il8m9ZN27O2tGSuIGAuwqJTSV7+lbuA+h/ODVyVYAAeveuBD2r1AKAG7KBqT8KChyyqxsoJbgV9N7MOwgs2fXImL7SSeH/A4LjKLqCmyeMSRQwsAqtHra6JN8y4zolDqewWStVD/rfBkGKqPU/Rh9gvBdbBd/QnenI4JFH/rRGqIbmggI7QQKBgQDvulrA5oO1XzY9iWyIdGurT6CYh/djdATs/hWhxs2ojcvkSD3OJS7ZtmURr779TANa/cD8P+SVXTF8zK2xelKq9/nVsgHqbQONwk20s34l+OvGPHM44eiN2ByUhwjKUY5rWAZLNfHCDrbzhGjoFYi2V0HOPr/dL9E0GBw4WFC3ewKBgQC+QomeAM5EVdehDnU2m8BQDjz/HW1X79jFUpD1u2+9dh4NKSO3jl+sb2OoGIiIrQvys1Lk7tMUkGi0wl1iW4Xh3VLcckC0Oj0BPIhuiI6+VhaWnHs0c8z9UyXxR45ajUCdh6gpUe3LHiP8TExv/VuVuWxYa2yjvljAL8MtvQIy8QKBgHHaKoHUuJap+mOtYZtI113L+nSWlnpoPnKjlXh4p3G2ri/j016VMDP7uPJ2ahl4QVu0ijQC0DfTw84GkzNn6d0C2meQG/LPBVINBsScPnwgRTGnwf0w5TaAfcpYe2dC5oW6j3qmywOj42Wx6/TVTVF0DW+7Q3nz3MKF9NkZsHQDAoGBALWrbyByAIL7vMPKvF2CZyWKEvA+txPKr5/q8QPTECVGzfcfGz3AMtbCHdR6aJSpN8ltNW/oI6F1KqW/GdA7OCGtZdudNO9i1LGDjXqN5k8UgMkbbgFtuYUCii5Tdzg7vEEvMYES/A6BHzdf23Tr5TGYNj5PhRLtRG1urd3Upm7hAoGAFeNtal5LLiD8BaISWhulUhdbPG7QOFaizFngknRm7Pgr2pabTPT2miplVBdWg+mNQepIFF3rG8Jv1ARO9l6FLyZUvaI5ZVaes6aJP1CakZ37S5HFxYZ7eGzhn9zyOBXH1LiywjYdGCSAN6hAAfAxbSQCTHhBc+ccX+Xk15bXKN8=
      """;
  private final String secretKey;

  private RSAUtil(String secretKey) {
    this.secretKey = secretKey;
  }

  private static void generateKey() throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
    keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
    String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
    System.out.println(publicKey);
    System.out.println("-------------------------------------------");
    System.out.println(privateKey);
  }

  public static RSAUtil publicKey() {
    return new RSAUtil(PUBLIC_KEY.trim());
  }

  public static RSAUtil privateKey() {
    return new RSAUtil(PRIVATE_KEY.trim());
  }

  /**
   * RSA加密
   *
   * @param data
   * @return
   * @throws Exception
   */
  public String encrypt(String data) throws Exception {
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    byte[] secretKey = Base64.getDecoder().decode(this.secretKey.getBytes(StandardCharsets.UTF_8)), decode = data.getBytes(StandardCharsets.UTF_8), cache;
    if (this.secretKey.equals(PUBLIC_KEY.trim())) cipher.init(Cipher.ENCRYPT_MODE, KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(secretKey)));
    else cipher.init(Cipher.ENCRYPT_MODE, KeyFactory.getInstance(ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(secretKey)));
    int length = decode.length, offSet = 0, i = 0;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    // 对数据分段加密
    while (length - offSet > 0) {
      if (length - offSet > MAX_ENCRYPT_BLOCK) cache = cipher.doFinal(decode, offSet, MAX_ENCRYPT_BLOCK);
      else cache = cipher.doFinal(decode, offSet, length - offSet);
      out.write(cache, 0, cache.length);
      i++;
      offSet = i * MAX_ENCRYPT_BLOCK;
    }
    byte[] result = out.toByteArray();
    out.close();
    return Base64.getEncoder().encodeToString(result);
  }

  /**
   * RSA解密
   *
   * @param data
   * @return
   * @throws Exception
   */
  public String decrypt(String data) throws Exception {
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    byte[] secretKey = Base64.getDecoder().decode(this.secretKey.getBytes(StandardCharsets.UTF_8)),
        decode = Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8)), cache;
    if (this.secretKey.equals(PUBLIC_KEY.trim())) cipher.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(secretKey)));
    else cipher.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance(ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(secretKey)));
    int length = decode.length, offSet = 0, i = 0;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    // 对数据分段加密
    while (length - offSet > 0) {
      if (length - offSet > MAX_DECRYPT_BLOCK) cache = cipher.doFinal(decode, offSet, MAX_DECRYPT_BLOCK);
      else cache = cipher.doFinal(decode, offSet, length - offSet);
      out.write(cache, 0, cache.length);
      i++;
      offSet = i * MAX_DECRYPT_BLOCK;
    }
    out.close();
    return out.toString();
  }
}
